/*
 Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.

 https://www.cocos.com/

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights to
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 of the Software, and to permit persons to whom the Software is furnished to do so,
 subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

import {
    _decorator, Node, Button, Toggle, EditBox,
    Label, Sprite, Slider, math, SpriteFrame, v3, v2, Vec2,
} from 'cc';
import { Bind } from '../bind/bind';
import { Sound } from '../audio/sound';
import { Msg } from '../../util/msg';
import { ResCache } from '../res/res-cache';
import { GMath } from '../../util/g-math';
import { UtilNode, UtilVec3 } from '../../util/util';
import { FilSmooth } from './fil-smooth';
import { Guide } from './guide';

export class UIBase {

    public node: Node;

    public isOn = false;

    public _map: UICom[] = Object.create(null);

    constructor (node: Node) {
        this.node = node;
        this._map = BindUI.get(this.node);
    }

    public refresh (): void {
        if (!this.isOn) return;
        for (let i = 0; i < this._map.length; i++)
            this._map[i].refresh();
    }

    public on (): void {
        this.isOn = true;
        for (let i = 0; i < this._map.length; i++)
            this._map[i].on();
        this.node.active = true;
    }

    public off (): void {
        this.isOn = false;
        for (let i = 0; i < this._map.length; i++)
            this._map[i].off();
        this.node.active = false;
    }

    public destroy (): void { }
}

export class UICom {

    protected _node: Node;

    constructor (node: Node) {
        this._node = node;
    }

    public on (): void { }

    public off (): void { }

    public refresh (): void { }

}


export class BindUI {

    // User interface binding mapping.
    private static _map: { [com: string]: (node: Node) => UICom } = {};

    /**
     * Initialize the user interface binder.
     */
    public static init () {

        //绑定按钮游戏对象节点。
        this._map['btn'] = (node: Node) => new BtnBase(node);

        //绑定标签游戏对象节点。
        this._map['txt'] = (node: Node) => new TxtBase(node);

        //绑定组游戏对象节点。
        this._map['grp'] = (node: Node) => new GrpBase(node);

        //绑定精灵游戏对象节点。
        this._map['spr'] = (node: Node) => new SprBase(node);

        //绑定切换游戏对象节点。
        this._map['tgl'] = (node: Node) => new TglBase(node);

        //绑定滑动条游戏对象节点。
        this._map['sli'] = (node: Node) => new SliBase(node);

        //绑定填充精灵游戏对象节点。
        this._map['fil'] = (node: Node) => new FilBase(node);

        //绑定引导ui
        this._map['grp_guide'] = (node: Node) => new GrpGuide(node);

    }

    /**
     * This method is used to detect and bind the nodes of the user interface.
     * @param node Binding nodes need to be detected.
     * @returns Array of components that have been bound.
     */
    public static get (node: Node): UICom[] {

        var children = UtilNode.getChildren(node);
        var comList: UICom[] = [];
        console.log(children,"children")

        for (let i = 0; i < children.length; i++) {
            const tempi = children[i];

            if (this._map[tempi.name]) {
                // Bind key
                const key = tempi.name;
                const com = this._map[key];
                if (com !== undefined) {
                    comList.push(this._map[key](tempi));
                    continue;
                }
            }
            if (Bind.Instance.hasBind(tempi.name)) {
                // Bind type
                const type = tempi.name.slice(0, 3);
                const comType = this._map[type];
                if (comType) {
                    comList.push(comType(tempi));
                }
            }
        }

        return comList;
    }

}

export class BtnBase extends UICom {
    private _name: string = '';
    private _btn: Button | null | undefined;
    constructor (node: Node) {
        super(node);
        let self = this;
        this._name = node.name;
        this._btn = self._node?.getComponent(Button);
        this._node?.on(Button.EventType.CLICK, () => {
            Bind.Instance.on(self._name);
            Sound.on('sfx_btn_click');
        }, this);
    }
}

export class TxtBase extends UICom {
    text: Label;
    constructor (node: Node) {
        super(node);
        this.text = UtilNode.getComponent(this._node, Label);
        this.text.string = Bind.Instance.get(this._node?.name);
    }

    public on (): void {
        super.on();
        this.refresh();
    }

    public refresh (): void {
        super.refresh();
        this.text!.string = Bind.Instance.get(this._node!.name);
    }
}

export class SliBase extends UICom {
    slider: Slider;
    fill: Sprite;
    constructor (node: Node) {
        super(node);
        this.slider = UtilNode.getComponent(this._node, Slider);
        this.fill = UtilNode.getChildComponent(this._node, 'fill', Sprite);
        if (this.fill.type !== Sprite.Type.FILLED) throw new Error(`${this._node.name} node Sprite not set Sprite.Type.FILLED`);
        var defaultValue = Bind.Instance.get(this._node.name);
        this.slider.progress = defaultValue;
        this.fill!.fillRange = defaultValue;
        this.slider?.node.on('slide', () => {
            this.fill!.fillRange = GMath.range(1, 0, this.slider!.progress);
            Msg.emit(this._node.name, this.slider?.progress);
        }, this);
    }

    public on (): void {
        super.on();
        var defaultValue = Bind.Instance.get(this._node.name);
        this.slider!.progress = defaultValue;
        this.fill!.fillRange = defaultValue;
    }
}

//本类用于实现滑动条的平滑过渡
export class FilBase extends UICom {
    fil_value: Sprite;
    fil_smooth: FilSmooth;
    constructor (node: Node) {
        super(node);
        this.fil_value = UtilNode.getComponent(this._node, Sprite);
        this.fil_value.fillRange = 0;
        this.fil_smooth = this._node.addComponent(FilSmooth);
        Msg.on(this._node.name, (value: number) => {
            this.fil_smooth.setValue(value);
        });
    }

    public on (): void {
        this.fil_value!.fillRange = 0;
    }
}

export class SprBase extends UICom {
    sprite: Sprite;
    constructor (node: Node) {
        super(node);
        this.sprite = UtilNode.getComponent(this._node, Sprite);
        Msg.on(this._node.name, (value: SpriteFrame) => {
            this.sprite!.spriteFrame = value;
        })
    }

    public on (): void {
        super.on();
        const src = Bind.Instance.get(this._node.name);
        this.sprite.spriteFrame = src;
    }
}

export class GrpBase extends UICom {
    constructor (node: Node) {
        super(node);
    }
}


export class TglBase extends UICom {
    private _toggle: Toggle;
    constructor (node: Node) {
        super(node);
        this._toggle = UtilNode.getComponent(this._node, Toggle);
        this._node.on(Toggle.EventType.TOGGLE, () => {

            Bind.Instance.on(this._node.name);
        })
    }
}





export class GrpGuide extends GrpBase {

    constructor (node: Node) {
        super(node);
        Msg.on('guide_refresh', this.guide_refresh.bind(this));
    }

    public on (): void {
        this.guide_refresh();
    }

    guide_refresh () {

        // close all
        for (var i = 0; i < this._node.children.length; i++)
            this._node.children[i].active = false;

        var n = this._node.getChildByName(Guide.Instance._cur_name);
        if (n == undefined) {
            console.error('error guide name:', Guide.Instance._cur_name);
        } else {
            n.active = true;
            Sound.on('sfx_notify_tip');
        }
    }

}
